#include <stdio.h>
#include <string.h>    //用于 strcpy 和 strlen 函数，处理字符串。
#include <unistd.h>    //用于 fork、close 和 write 函数，进行进程控制和文件操作。
#include <sys/types.h> //与 open 函数配合使用
#include <sys/stat.h>  //与 open 函数配合使用
#include <fcntl.h>     //用于 open 函数的标志定义，如 O_WRONLY、O_TRUNC、O_CREAT。
#include <assert.h>    //断言
#include <stdlib.h>    //用于 malloc 和 free 函数，进行内存分配和释放。

#define NUM 1024
// 定义一个结构体，用于存储文件描述符、缓冲区和缓冲区中有效数据的结束位置
struct MyFILE_
{
    int fd;           // 文件描述符
    char buffer[NUM]; // 缓冲区
    int end;          // 缓冲区中数据的结束位置
};

typedef struct MyFILE_ MyFILE;

// 打开文件，模仿fopen函数
MyFILE *fopen_(const char *pathname, const char *mode)
{
    assert(pathname); // 检查路径指针不为空
    assert(mode);     // 检查模式指针不为空

    MyFILE *fp = NULL; // 初始化返回的文件结构体指针为 NULL

    int flags = 0;                                            // 初始化文件打开标志
    mode_t mode_mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 设置文件权限掩码

    // 根据给定的模式设置文件打开标志
    if (strcmp(mode, "r") == 0)
    {
        flags = O_RDONLY;
    }
    else if (strcmp(mode, "r+") == 0)
    {
        flags = O_RDWR;
    }
    else if (strcmp(mode, "w") == 0)
    {
        flags = O_WRONLY | O_CREAT | O_TRUNC;
    }
    else if (strcmp(mode, "w+") == 0)
    {
        flags = O_RDWR | O_CREAT | O_TRUNC;
    }
    else if (strcmp(mode, "a") == 0)
    {
        flags = O_WRONLY | O_CREAT | O_APPEND;
    }
    else if (strcmp(mode, "a+") == 0)
    {
        flags = O_RDWR | O_CREAT | O_APPEND;
    }
    else
    {
        fprintf(stderr, "Unsupported mode: %s\n", mode); // 输出不支持的模式提示
        return NULL;                                     // 返回 NULL 表示失败
    }

    // 如果设置了有效的文件打开标志
    if (flags != 0)
    {
        // 使用 open 函数打开文件
        int fd = open(pathname, flags, mode_mask);
        if (fd >= 0)
        {
            // 分配内存并初始化文件结构体
            fp = (MyFILE *)malloc(sizeof(MyFILE));
            if (fp != NULL)
            {
                fp->fd = fd;                // 设置文件描述符
                fp->end = 0;                // 初始化缓冲区结束位置
                memset(fp->buffer, 0, NUM); // 清除缓冲区
            }
            else
            {
                // 内存分配失败，关闭文件描述符
                close(fd);
            }
        }
        else
        {
            // 打开文件失败，输出错误信息
            perror("Error opening file");
        }
    }
    return fp; // 返回文件结构体指针
}

// 将消息写入文件，模仿fputs函数
void fputs_(const char *message, MyFILE *fp)
{
    assert(message != NULL);
    assert(fp != NULL);

    // 如果缓冲区还有空间，则直接写入缓冲区
    if (fp->end + strlen(message) <= NUM)
    {
        // 复制消息到缓冲区
        strcpy(fp->buffer + fp->end, message);
        fp->end += strlen(message);
    }
    else
    {
        // 如果缓冲区空间不足，刷新缓冲区并尝试再次写入
        write(fp->fd, fp->buffer, fp->end);
        fp->end = 0;
        strcpy(fp->buffer, message);
        fp->end += strlen(message);
    }

    // 特殊处理标准输入/输出/错误
    if (fp->fd == 0)
    {
        // 不做特殊处理
    }
    else if (fp->fd == 1)
    {
        // 如果缓冲区末尾是换行符，则刷新缓冲区
        if (fp->buffer[fp->end - 1] == '\n')
        {
            write(fp->fd, fp->buffer, fp->end);
            fp->end = 0;
        }
    }
    else if (fp->fd == 2)
    {
        // 不做特殊处理
    }
    else
    {
        // 对于其他文件，如果缓冲区满，则刷新缓冲区
        if (fp->end == NUM)
        {
            write(fp->fd, fp->buffer, fp->end);
            fp->end = 0;
        }
    }
}

void fflush_(MyFILE *fp)
{
    assert(fp != NULL);

    // 如果缓冲区中有未写入的数据，则刷新缓冲区
    if (fp->end > 0)
    {
        write(fp->fd, fp->buffer, fp->end);
        fsync(fp->fd);
        fp->end = 0;
    }
}

// 关闭文件，模仿fclose函数
void fclose_(MyFILE *fp)
{
    assert(fp);
    fflush_(fp);   // 刷新缓冲区
    close(fp->fd); // 关闭文件描述符
    free(fp);      // 释放分配的内存
}

int main(void)
{
    MyFILE *fp = fopen_("test.txt", "w+");
    if (!fp)
    {
        perror("Failed to open the file");
        exit(EXIT_FAILURE);
    }

    fputs_("Hello, World!\n", fp);
    fputs_("This is a test.\n", fp);

    fclose_(fp);

    return EXIT_SUCCESS;
}